/*
 * mhcontext.cpp
 *
 *  Created on: 2010-8-24
 *      Author: angelstone
 */

#include <wx/wx.h>
#include <wx/filename.h>

#include "soci.h"
#include "soci-sqlite3.h"
#include <sqlite3.h>

#include "mhdefs.h"
#include "mhcontext.h"
#include "mhcategory.h"
#include "mhmedia.h"

#include "addon/mhaddon.h"
#include "addon/mhmediaaddon.h"
#include "addon/mhscanneraddon.h"

#include "mhdirscanner.h"
#include "mhfilescanner.h"
#include "mhtxtmedia.h"

using namespace mediahouse;

CMHContext::CMHContext() :
	m_DatabaseName(wxT("mediahouse.db")) {
	m_pDatabase = new hiberlite::Database(
			(const char *) GetDatabaseName().ToUTF8());
	m_pDatabase->registerBeanClass<mediahouse::CMHMedia> ();
	m_pDatabase->registerBeanClass<mediahouse::CMHCategory> ();

	try {
		m_pDatabase->createModel();
	} catch (hiberlite::database_error & e) {

	}

	m_pSqlSession = new soci::session(soci::sqlite3_backend,
			(const char *) GetDatabaseName().ToUTF8());

	m_pSavedConn
			= ((soci::sqlite3_session_backend *) m_pSqlSession->get_backend())->conn_;
	((soci::sqlite3_session_backend *) m_pSqlSession->get_backend())->conn_
			= m_pDatabase->getConnection()->getSQLite3Ptr();

	LoadAllAddons();
}

CMHContext::~CMHContext() {
	CleanUpAddons();

	((soci::sqlite3_session_backend *) m_pSqlSession->get_backend())->conn_
			= (sqlite3*) m_pSavedConn;

	delete m_pSqlSession;
	delete m_pDatabase;
}

int CMHContext::ScanItem(const wxString & scanPath, void * cookie) {
	CMHScannerAddon * p_scanner = FindScanner(scanPath);

	if (!p_scanner)
		return 0;

	int ret = p_scanner->ScanPath(this, scanPath, cookie);

	delete p_scanner;

	return ret;
}

CMHScannerAddon * CMHContext::FindScanner(const wxString & scanPath) {
	if (wxDirExists(scanPath)) {
		return new CMHDirScanner();
	}

	if (wxFileExists(scanPath)) {
		return new CMHFileScanner();
	}

	return NULL;
}

bool CMHContext::FindMediaByLocation(const wxString & location,
		hiberlite::bean_ptr<mediahouse::CMHMedia> & media) {
	int id;

	try {
		soci::indicator ind = soci::i_null;

		std::string loc((const char*) location.ToUTF8());

		(*m_pSqlSession)
				<< "select hiberlite_id from CMHMedia where m_Location= :loc", soci::into(
				id, ind), soci::use(loc, "loc");

		if (!m_pSqlSession->got_data()) {
			return false;
		}
	} catch (soci::soci_error const & e) {
		wxLogError(_("FindMediaByLocation:%s"), e.what());

		return false;
	}

	media = m_pDatabase->loadBean<mediahouse::CMHMedia> (id);

	return true;
}

void CMHContext::NotifyTotalScanItems(int count, void * cookie,
		const wxString & msg) {
	wxThreadEvent event(wxEVT_COMMAND_THREAD, mediahouse::MH_ID_PROGRESS_SET_MAX);

	event.SetInt(count);

	if (!msg.IsEmpty())
		event.SetString(msg);

	wxQueueEvent((wxWindow *) cookie, event.Clone());
}

void CMHContext::NotifyItemScaned(int count, void * cookie,
		const wxString & msg, bool delta) {
	wxThreadEvent event(wxEVT_COMMAND_THREAD,
			delta ? mediahouse::MH_ID_PROGRESS_UPDATED_DELTA
					: mediahouse::MH_ID_PROGRESS_UPDATED);

	event.SetInt(count);

	if (!msg.IsEmpty())
		event.SetString(msg);

	wxQueueEvent((wxWindow *) cookie, event.Clone());
}

int CMHContext::HandleMedia(
		hiberlite::bean_ptr<mediahouse::CMHMedia> & p_media, void * cookies) {
	CMHAddonArray::iterator it;
	int handled = 0;

	for (it = m_Addons.begin(); it != m_Addons.end(); it++) {
		CMHMediaAddon * pMediaAddon = dynamic_cast<CMHMediaAddon *> (*it);

		if (pMediaAddon->CanHandleMedia(this, p_media, cookies)) {
			handled = pMediaAddon->HandleMedia(this, p_media, cookies);

			if (handled)
				break;
		}
	}

	//clear modified flag here when not handled by any media addon
	if (!handled)
		p_media->SetModified((wxLongLong) 0);

	return handled;
}

void CMHContext::LoadAllAddons() {
	CMHAddon * pAddon = new CMHTxtMediaAddon();
	pAddon->Initialize(this);

	m_Addons.push_back(pAddon);
}

void CMHContext::CleanUpAddons() {
	CMHAddonArray::iterator it;

	for (it = m_Addons.begin(); it != m_Addons.end(); it++) {
		(*it)->Deinitialize();
		delete (*it);
	}

	m_Addons.Clear();
}

bool CMHContext::FindCategoryFullId(const wxString & path, wxString & full_id,
		bool create) {
	wxFileName fname;
	fname.AssignDir(path);

	const wxArrayString & dirs = fname.GetDirs();

	wxArrayString::const_iterator it;

	full_id = wxEmptyString;

	wxInt32 parent_id = 0;

	for (it = dirs.begin(); it != dirs.end(); it++) {
		wxInt32 id = -1;

		try {
			soci::indicator ind;

			std::string name((const char*) (*it).ToUTF8());
			int p_id = parent_id;

			(*m_pSqlSession)
					<< "select hiberlite_id from CMHCategory where m_Name = :name and m_ParentId = :p_id", soci::into(
					id, ind), soci::use(name), soci::use(p_id);

			if (!m_pSqlSession->got_data()) {
				id = -1;
			}
		} catch (soci::soci_error const & e) {
			wxLogError(_("FindCategoryFullId:%s"), e.what());

			return false;
		}

		if (id == -1 && !create)
			return false;

		if (id == -1) {
			hiberlite::bean_ptr<mediahouse::CMHCategory> c =
					GetDatabase()->createBean<mediahouse::CMHCategory> ();
			c->SetDisplayName(*it);
			c->SetName(*it);
			c->SetParentId(parent_id);
			c.save();

			id = c.get_id();
		}

		full_id.Append(wxString::Format(wxT("/%d"), id));

		parent_id = id;
	}

	return true;
}

bool CMHContext::FindCategoryId(const wxString & name, wxInt32 & id,
		bool create) {
	wxString full_id = wxEmptyString;

	if (!FindCategoryFullId(name, full_id, create))
		return false;

	wxFileName fname;
	fname.Assign(full_id);

	long int val = 0;

	if (!fname.GetFullName().ToLong(&val, 10))
		return false;

	id = val;

	return true;
}

void CMHContext::CancelTask(bool cancel) {
	wxCriticalSectionLocker lock(m_csCancelled);

	m_cancelled = cancel;
}

bool CMHContext::Cancelled() {
	wxCriticalSectionLocker lock(m_csCancelled);

	return m_cancelled;
}
